package com.sunnada.nginx.service;

import com.github.odiszapc.nginxparser.NgxBlock;
import com.github.odiszapc.nginxparser.NgxConfig;
import com.github.odiszapc.nginxparser.NgxEntry;
import com.sunnada.nginx.bean.nginx.http.HttpServer;
import com.sunnada.nginx.common.NginxUtils;
import com.sunnada.nginx.exception.CustonException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service("listenerService")
public class ListenerServiceImpl implements ListenerService {

    @Autowired
    NginxUtils nginxUtils;

    @Autowired
    StatusService statusService;

    /**
     * 获取配置中的http-server列表
     */
    public List<HttpServer> list() throws CustonException {
        List<HttpServer> result = new ArrayList<>();
        NgxConfig conf = nginxUtils.read();
        List<NgxEntry> findAll = conf.findAll(NgxConfig.BLOCK, "http", "server");
        for (NgxEntry ngxEntry : findAll) {
            NgxBlock block = (NgxBlock) ngxEntry;
            HttpServer httpServer = new HttpServer(block);
            result.add(httpServer);
        }
        return result;
    }

    public HttpServer one(String nameAndPort) throws CustonException {
        NgxConfig conf = nginxUtils.read();
        List<NgxEntry> findAll = conf.findAll(NgxConfig.BLOCK, "http", "server");
        for (NgxEntry ngxEntry : findAll) {
            NgxBlock block = (NgxBlock) ngxEntry;
            HttpServer httpServer = new HttpServer(block);
            if (httpServer.getNewNameAndPort().equals(nameAndPort)) return httpServer;
        }
        throw new CustonException("查询的server块不存在");
    }

    public boolean post(HttpServer httpServer) throws CustonException {
        httpServer.vali();
        // 读取配置
        NgxConfig conf = nginxUtils.read();
        // 备份配置
        String backConf = NginxUtils.toString(conf);
        // 判断httpServer是否存在
        // 存在则抛出异常，不存在则执行添加操作
        if (httpServerIsExistInConf(conf, httpServer.getNewNameAndPort()))
            throw new CustonException("该httpServer已存在，无法新增");
        // 添加upstream
        addHttpServer(conf, httpServer);
        checkConf(conf, backConf);
        return true;
    }

    private void addHttpServer(NgxConfig conf, HttpServer httpServer) {
        NgxBlock httpBlock = (NgxBlock) conf.find(NgxConfig.BLOCK, "http");
        httpBlock.addEntry(httpServer.toNgxBlock());
    }

    public boolean put(HttpServer httpServer) throws CustonException {
        httpServer.vali();
        // 读取配置
        NgxConfig conf = nginxUtils.read();
        // 备份配置
        String backConf = NginxUtils.toString(conf);
        // 判断httpServer是否存在
        // 不存在则抛出异常，存在则执行添加操作
        if (!httpServerIsExistInConf(conf, httpServer)) throw new CustonException("该httpServer不存在，无法修改");
        // 修改配置中的同名httpServer
        updateHttpServer(conf, httpServer);
        checkConf(conf, backConf);
        return true;
    }

    private void updateHttpServer(NgxConfig conf, HttpServer httpServer) {
        NgxBlock httpBlock = (NgxBlock) conf.find(NgxConfig.BLOCK, "http");
        deleteHttpServer(conf, httpServer);
        httpBlock.addEntry(httpServer.toNgxBlock());
    }

    public boolean delete(String nameAndPort) throws CustonException {
        // 读取配置
        NgxConfig conf = nginxUtils.read();
        // 备份配置
        String backConf = NginxUtils.toString(conf);
        // 判断httpServer是否存在
        // 不存在则抛出异常，存在则执行添加操作
        if (!httpServerIsExistInConf(conf, nameAndPort)) throw new CustonException("该httpServer不存在，无法删除");
        // 删除配置中的同名HttpServer
        deleteHttpServer(conf, nameAndPort);
        checkConf(conf, backConf);
        return true;
    }

    /**
     * @see ListenerServiceImpl#deleteHttpServer(NgxConfig, String)
     */
    private void deleteHttpServer(NgxConfig conf, HttpServer httpServer) {
        deleteHttpServer(conf, httpServer.getNameAndPort());
    }

    private void deleteHttpServer(NgxConfig conf, String nameAndPort) {
        NgxBlock httpBlock = (NgxBlock) conf.find(NgxConfig.BLOCK, "http");
        List<NgxEntry> findAll = conf.findAll(NgxConfig.BLOCK, "http", "server");
        for (NgxEntry entry : findAll) {
            HttpServer temp = new HttpServer((NgxBlock) entry);
            if (temp.getNameAndPort().equals(nameAndPort)) {
                httpBlock.remove(entry);
                return;
            }
        }
    }

    private boolean httpServerIsExistInConf(NgxConfig conf, HttpServer httpServer) {
        return httpServerIsExistInConf(conf, httpServer.getNameAndPort());
    }

    private boolean httpServerIsExistInConf(NgxConfig conf, String nameAndPort) {
        List<NgxEntry> findAll = conf.findAll(NgxConfig.BLOCK, "http", "server");
        for (NgxEntry ngxEntry : findAll) {
            NgxBlock block = (NgxBlock) ngxEntry;
            HttpServer temp = new HttpServer(block);
            if (!temp.getNameAndPort().equals(nameAndPort)) continue;
            return true;
        }
        return false;
    }

    /**
     * 使用新的配置重启ngixn，如果重启失败，则回滚配置
     *
     * @param conf 新配置
     * @param back 配置备份
     */
    private void checkConf(NgxConfig conf, String back) throws CustonException {
        try {
            nginxUtils.save(conf);
            //重启Nginx
            statusService.checkConf();
        } catch (Exception e) {
            //恢复到上一次配置
            nginxUtils.save(back);
            throw new CustonException("已恢复到上一次配置");
        }
    }
}
